/*
 * The Open Source Unix Installer
 *
 * The MIT License
 *
 * Copyright 2011-2012 Andrey Pudov.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*
 * @author  Andrey Pudov    <andrey@andreypudov.com>
 * @version 0.00.00
 * @name    u_processor.h
 * @date:   Jun 17, 2012
 */

#ifndef INSTALLER_PROC_U_PROCESSOR_H_
#define INSTALLER_PROC_U_PROCESSOR_H_

#include <stdbool.h>

#include "processor.h"

#include "u_main.h"

#ifdef __cplusplus
extern "C" {
#endif

char* _u_installer_processor_read_cpuinfo(char *property);

bool u_installer_processor_start();                 /* unit entry point */

bool u_installer_processor_get_name();
bool u_installer_processor_get_vendor();
bool u_installer_processor_get_vendor_id();
bool u_installer_processor_get_stepping();
bool u_installer_processor_get_model();
bool u_installer_processor_get_family();
bool u_installer_processor_get_flags();

bool u_installer_processor_start() {
    return (u_installer_processor_get_name()
        && u_installer_processor_get_vendor()
        && u_installer_processor_get_vendor_id()
        && u_installer_processor_get_stepping()
        && u_installer_processor_get_model()
        && u_installer_processor_get_family()
        && u_installer_processor_get_flags());
}

bool u_installer_processor_get_name() {
    installer_processor_get_name();
    u_installer_main_print("processor", "get_name()", true);
    
    return true;
}

bool u_installer_processor_get_vendor() {
    char *value = _u_installer_processor_read_cpuinfo("vendor_id");
    char *unit  = installer_processor_get_vendor();
    
    if ((value != NULL) && (unit != NULL) && (strcmp(value, unit) == 0)) {
        u_installer_main_print("processor", "get_vendor()", true);
        
        free(value);
        free(unit);
        
        return true;
    }
    
    u_installer_main_print("processor", "get_vendor()", false);
    u_installer_main_trace("processor", "get_vendor()", 
                           "\tvendor id:   %s\n" \
                           "\tvalue    :   %s\n",
                           value,
                           unit);
    
    return false;
}

bool u_installer_processor_get_vendor_id() {
    char *value    = _u_installer_processor_read_cpuinfo("vendor_id");
    char ids[][13] = {"AMDisbetter!", "AuthenticAMD", "CentaurHauls",
                      "CyrixInstead", "GenuineIntel", "TransmetaCPU",
                       "GenuineTMx86", "Geode by NSC", "NexGenDriven",
                       "RiseRiseRise", "Sis SiS SiS ", "UMC UMC UMC ",
                       "VIA VIA VIA ", "Vortex86 SoC"};
    int  unit      = installer_processor_get_vendor_id();
    
    if (value == NULL) {
        u_installer_main_print("processor", "get_vendor_id()", false);
        
        return false;
    }
    
    /* 13 is the length of vendor identification string */
    for (unsigned int index = 0; index < sizeof(ids) / 13; ++index) {
        if ((strcmp(value, ids[index]) == 0) && (index == unit)) {
            u_installer_main_print("processor", "get_vendor_id()", true);
            
            free(value);
        
            return true;
        }
    }
    
    return false;
}

bool u_installer_processor_get_stepping() {
    char *value    = _u_installer_processor_read_cpuinfo("stepping");
    char  unit[80] = {0};
    
    sprintf(unit, "%d", installer_processor_get_stepping());
    
    if ((value != NULL) && (unit != NULL) && (strcmp(value, unit) == 0)) {
        u_installer_main_print("processor", "get_stepping()", true);
        
        free(value);
        
        return true;
    }
    
    u_installer_main_print("processor", "get_stepping()", false);
    u_installer_main_trace("processor", "get_stepping()", 
                           "\tstepping:   %s\n" \
                           "\tvalue   :   %s\n",
                           value,
                           unit);
    
    return false;
}

bool u_installer_processor_get_model() {
    char *value    = _u_installer_processor_read_cpuinfo("model");
    char  unit[80] = {0};
    
    sprintf(unit, "%d", installer_processor_get_model());
    
    if ((value != NULL) && (unit != NULL) && (strcmp(value, unit) == 0)) {
        u_installer_main_print("processor", "get_model()", true);
        
        free(value);
        
        return true;
    }
    
    u_installer_main_print("processor", "get_model()", false);
    u_installer_main_trace("processor", "get_model()", 
                           "\tmodel:   %s\n" \
                           "\tvalue:   %s\n",
                           value,
                           unit);
    
    return false;
}

bool u_installer_processor_get_family() {
    char *value    = _u_installer_processor_read_cpuinfo("cpu family");
    char  unit[80] = {80};
    
    sprintf(unit, "%d", installer_processor_get_family());
    
    if ((value != NULL) && (unit != NULL) && (strcmp(value, unit) == 0)) {
        u_installer_main_print("processor", "get_family()", true);
        
        free(value);
        
        return true;
    }
    
    u_installer_main_print("processor", "get_family()", false);
    u_installer_main_trace("processor", "get_family()", 
                           "\tfamily:   %s\n" \
                           "\tvalue :   %s\n",
                           value,
                           unit);
    
    return false;
}

bool u_installer_processor_get_flags() {
    int  *unit    = installer_processor_get_flags();
    char *flags[] = {"sse3", "pclmuldq", "dtes64", "monitor", "ds_cpl", "vmx", 
                     "smx", "eist", "tm2", "ssse3", "cnxt_id", "", "fma",
                     "cx16", "xtpr", "pdcm", "", "pcid", "dca", "sse4.1", 
                     "sse4.2", "x2apic", "movbe", "popcnt", "tcs_deadline",
                     "aes", "xsave", "osxsave", "avx", "f16c", "rdrand", "",
                     
                     "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
                     "cx8", "apic", "", "sep", "mtrr", "pge", "mca", "cmov",
                     "pat", "pse36", "psm", "clflush", "ds", "", "acpi", "mmx",
                     "fxsr", "sse", "sse2", "ss", "htt", "tm", "", "pre"};
    char *value   = _u_installer_processor_read_cpuinfo("flags");
    
    printf("%s\n", value);
    for (unsigned int index = 0; index < 64; ++index) {
        if ((unit[index] > 0) && (strstr(value, flags[index]) != 0)) {
            printf("%2d %-14s - found\n", index, flags[index]);
        }
        
        if ((unit[index] > 0) && (strstr(value, flags[index]) == 0)) {
            printf("%2d %-14s - not found\n", index, flags[index]);
        }
    }
    
    u_installer_main_print("processor", "get_flags()", false);
    
    return false;
}

/*
 * Reads cpuinfo file and gets specified property.
 *
 * @returns the cpuinfo property value (new allocated string)
 */
char* _u_installer_processor_read_cpuinfo(char *property) {
    char BUFFER[1024] = {0};
    char *token       = NULL;

    FILE *pointer = fopen("/proc/cpuinfo", "r");    
    if (pointer == NULL) {
        return NULL;
    }
    
    while (fgets(BUFFER, sizeof BUFFER, pointer) != NULL) {
        if (strstr(BUFFER, property) != NULL) {
            /* 
             * terminate a string before new line character 
             * returned by fgets function 
             */
            BUFFER[strlen(BUFFER) - 1] = '\0';
            
            token = strtok(BUFFER, ":");
            token = strtok(NULL,   ":");
        
            fclose(pointer);
        
            /* 
             * returns a copy of property value shifted to 
             * one character left after space 
             */
            return (strdup(token + 1));
        }
    }
    
    return NULL;
}

#ifdef __cplusplus
}
#endif

#endif /* INSTALLER_PROC_U_PROCESSOR_H_ */ 
